package com.atguigu.yygh.hosp.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.yygh.hosp.repository.ScheduleMongoRepository;
import com.atguigu.yygh.hosp.service.DepartmentService;
import com.atguigu.yygh.hosp.service.HospitalService;
import com.atguigu.yygh.hosp.service.ScheduleService;
import com.atguigu.yygh.hosp.utils.HttpRequestHelper;
import com.atguigu.yygh.model.hosp.BookingRule;
import com.atguigu.yygh.model.hosp.Department;
import com.atguigu.yygh.model.hosp.Hospital;
import com.atguigu.yygh.model.hosp.Schedule;
import com.atguigu.yygh.vo.hosp.BookingScheduleRuleVo;
import com.atguigu.yygh.vo.hosp.ScheduleOrderVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;

import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author A
 * @version 1.0
 * @description: TODO
 * @date 2025/3/16 17:40
 */
@Service
public class ScheduleServiceImpl implements ScheduleService {
    @Autowired
    private ScheduleMongoRepository scheduleMongoRepository;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private HospitalService hospitalService;
    @Autowired
    private DepartmentService departmentService;

    @Override
    public void saveSchedule(HttpServletRequest request) {
        Map<String, Object> map = HttpRequestHelper.switchRequest(request);
        String jsonString = JSONObject.toJSONString(map);
        Schedule schedule = JSONObject.parseObject(jsonString, Schedule.class);
        String hoscode = request.getParameter("hoscode");
        String depcode = request.getParameter("depcode");
        String hosScheduleId = request.getParameter("hosScheduleId");
        Schedule scheduleExist = scheduleMongoRepository.findScheduleByHoscodeAndDepcodeAndHosScheduleId(hoscode, depcode, hosScheduleId);

        if (scheduleExist == null) {
            // 新增
            schedule.setCreateTime(new Date());
            schedule.setUpdateTime(new Date());
            schedule.setIsDeleted(0);
            scheduleMongoRepository.save(schedule);
        } else {
            // 更新
            schedule.setCreateTime(scheduleExist.getCreateTime());
            schedule.setUpdateTime(new Date());
            schedule.setIsDeleted(0);
            schedule.setId(scheduleExist.getId());
            scheduleMongoRepository.save(schedule);
        }

    }

    @Override
    public Page<Schedule> scheduleList(Map<String, Object> map) {
        String page = (String) map.get("page");
        String limit = (String) map.get("limit");
        String hoscode = (String) map.get("hoscode");
        Schedule schedule = new Schedule();
        schedule.setHoscode(hoscode);
        Example<Schedule> scheduleExample = Example.of(schedule);
        PageRequest pageRequest = PageRequest.of(Integer.parseInt(page) - 1, Integer.parseInt(limit));
        Page<Schedule> all = scheduleMongoRepository.findAll(scheduleExample, pageRequest);
        return all;
    }

    @Override
    public Map<String, Object> getScheduleRule(Integer page, Integer limit, String hoscode, String depcode) {
        Criteria criteria = Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode);
        //根据周几进行分组
        Aggregation totalAgg = Aggregation.newAggregation(
                Aggregation.match(criteria), //匹配条件
                Aggregation.group("workDate")
                        .first("workDate")
                        .as("workDate")
                        .count().as("count")
                        .sum("reservedNumber").as("reservedNumber")
                        .sum("availableNumber").as("availableNumber"),
                //排序
                Aggregation.sort(Sort.Direction.ASC, "workDate"),
                //实现分页
                Aggregation.skip((page - 1) * limit),
                Aggregation.limit(limit)

        );

        //根据周几分组聚合可预约数
        AggregationResults<BookingScheduleRuleVo> aggResults = mongoTemplate.aggregate(totalAgg, Schedule.class, BookingScheduleRuleVo.class);

        //当前页数据
        List<BookingScheduleRuleVo> results = aggResults.getMappedResults();

        //把当前页的日期换成星期几
        for (BookingScheduleRuleVo result : results) {
            Date workDate = result.getWorkDate();
            String dayOfWeek = getDayOfWeek(new DateTime(workDate));
            result.setDayOfWeek(dayOfWeek);
        }


        //获取总数
        Aggregation totalCountAgg = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")

        );
        AggregationResults<BookingScheduleRuleVo> aggregate = mongoTemplate.aggregate(totalCountAgg, Schedule.class, BookingScheduleRuleVo.class);
        int total = aggregate.getMappedResults().size();
        //设置总数据返回
        HashMap<String, Object> map = new HashMap<>();
        map.put("bookingScheduleRuleList", results);
        map.put("total", total);
        //获取医院名称
        String hospitalName = hospitalService.getHospitalName(hoscode);
        //其他基础数据
        Map<String, String> baseMap = new HashMap<>();
        baseMap.put("hosname", hospitalName);
        map.put("baseMap", baseMap);
        return map;
    }

    @Override
    public List<Schedule> getScheduleInfo(String hoscode, String depcode, String workDate) {

        List<Schedule> schedule = scheduleMongoRepository.findScheduleByHoscodeAndDepcodeAndWorkDate(hoscode, depcode, new DateTime(workDate).toDate());
        //封装医院名称、科室名称、日期对应星期到param属性中
        schedule.stream().forEach(item -> {
            packageSchedule(item);
        });
        return schedule;
    }

    //根据当前日期获取预约周期类的所有排班信息
    @Override
    public Map<String, Object> getBookingSchedule(Integer page, Integer limit, String hoscode, String depcode) {
        HashMap<String, Object> resultMap = new HashMap<>();
        //获取医院预约规则
        Hospital hospital = hospitalService.findHospitalByHoscode(hoscode);
        BookingRule bookingRule = hospital.getBookingRule();
        //获取预约周期分页数据
        IPage<Date> dateIPage = getBookingDateList(page, limit, bookingRule);

        //当前页可预约列表
        List<Date> dateList = dateIPage.getRecords();
        //获取可预约日期科室剩余预约数
        Criteria criteria = Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode).and("workDate").in(dateList);
        Aggregation agg = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")
                        .first("workDate")
                        .as("workDate")
                        .sum("reservedNumber").as("reservedNumber")
                        .sum("availableNumber").as("availableNumber")

        );
        AggregationResults<BookingScheduleRuleVo> aggResults = mongoTemplate.aggregate(agg, Schedule.class, BookingScheduleRuleVo.class);
        //分组后的结果
        List<BookingScheduleRuleVo> results = aggResults.getMappedResults();
        //合并数据 将统计数据ScheduleVo根据“安排日期”合并到BookingRuleVo
        Map<Date, BookingScheduleRuleVo> scheduleVoMap = new HashMap<>();
        if (!results.isEmpty()) {
            scheduleVoMap = results.stream().collect(Collectors.toMap(BookingScheduleRuleVo::getWorkDate, BookingScheduleRuleVo -> BookingScheduleRuleVo));
        }
        //获取可预约排班规则
        ArrayList<BookingScheduleRuleVo> bookingScheduleRuleVoList = new ArrayList<>();
        for (int i = 0, len = dateList.size(); i < len; i++) {
            //获取当前日期
            Date date = dateList.get(i);
            BookingScheduleRuleVo ruleVo = scheduleVoMap.get(date);
            if (ruleVo == null) {
                //说明当天没有排班信息,则创建空对象
                ruleVo = new BookingScheduleRuleVo();
                //-1表示当天无号
                ruleVo.setAvailableNumber(-1);
                ruleVo.setDocCount(0);
            }
            ruleVo.setWorkDate(date);
            ruleVo.setWorkDateMd(date);
            //计算当前周几
            ruleVo.setDayOfWeek(getDayOfWeek(new DateTime(date)));
            //最后一页最后一条记录为即将预约   状态 0：正常 1：即将放号 -1：当天已停止挂号
            if (i == len - 1 && page == dateIPage.getPages()) {
                ruleVo.setStatus(1);
            } else {
                ruleVo.setStatus(0);
            }
            //第一天当天预约如果过了停号时间， 不能预约
            if (i == 0 && page == 1){
                DateTime stopTimeDateTime = getDateTime(new Date(), bookingRule.getStopTime());
                if (stopTimeDateTime.isBeforeNow()){
                    ruleVo.setStatus(-1);
                }
            }
            bookingScheduleRuleVoList.add(ruleVo);
        }
        //封装数据
        resultMap.put("bookingScheduleList", bookingScheduleRuleVoList);
        resultMap.put("total",dateIPage.getTotal());
        //其他基础数据
        HashMap<String, String> baseMap = new HashMap<>();
        //医院名称
        baseMap.put("hosname", hospitalService.getHospitalName(hoscode));
        //查询科室
        Department department =departmentService.getDepartment(hoscode,depcode);
        //大科室名称
        baseMap.put("bigname",department.getBigname());
        //科室名称
        baseMap.put("depname",department.getDepname());
        resultMap.put("baseMap", baseMap);
        //年月
        baseMap.put("workDateString",new DateTime(dateList.get(0)).toString("yyyy年MM月"));
        //放号时间
        baseMap.put("releaseTime",bookingRule.getReleaseTime());
        //停挂时间
        baseMap.put("stopTime",bookingRule.getStopTime());
        resultMap.put("baseMap", baseMap);
        return resultMap;
    }

    @Override
    public Schedule getSchedule(String scheduleId) {
        Schedule schedule = scheduleMongoRepository.findById(scheduleId).get();
        packageSchedule(schedule);


        return schedule;
    }

    @Override
    public ScheduleOrderVo getScheduleOrderVo(String scheduleId) {
        Schedule schedule = scheduleMongoRepository.findById(scheduleId).get();
        ScheduleOrderVo scheduleOrderVo = new ScheduleOrderVo();
        BeanUtils.copyProperties(schedule,scheduleOrderVo);
        scheduleOrderVo.setHosScheduleId(schedule.getHosScheduleId());
        scheduleOrderVo.setHosname(hospitalService.getHospitalName(schedule.getHoscode()));
        scheduleOrderVo.setDepname(departmentService.getDepartmentName(schedule.getHoscode(),schedule.getDepcode()));

        //安排日期reserveDat
        scheduleOrderVo.setReserveDate(schedule.getWorkDate());
        //"排班时间（0：上午 1：下午）"
        scheduleOrderVo.setReserveTime(schedule.getWorkTime());
        //先去hospital表中查询医院预约规则
        BookingRule boookingRule=hospitalService.getBoookingRule(schedule.getHoscode());
        //"退号时间"在预约规则中查询
        //退号时间为预约的时间前一天
        DateTime dateTime = new DateTime(schedule.getWorkDate()).plusDays(boookingRule.getQuitDay());
        //退号的年月日+退号时间
        DateTime quitDateTime = getDateTime(dateTime.toDate(), boookingRule.getQuitTime());
        scheduleOrderVo.setQuitTime(quitDateTime.toDate());
        //设置挂号结束时间
        DateTime stopDateTime = getDateTime(schedule.getWorkDate(), boookingRule.getStopTime());
        scheduleOrderVo.setStopTime(stopDateTime.toDate());
        return scheduleOrderVo;
    }

    @Override
    public Schedule getById(String scheduleId) {

       return scheduleMongoRepository.findById(scheduleId).get();

    }

    @Override
    public void update(Schedule schedule) {
        scheduleMongoRepository.save(schedule);
    }

    @Override
    public Schedule getByHosScheduleId(String scheduleId) {
     return   scheduleMongoRepository.findScheduleByHosScheduleId(scheduleId);
    }

    //获取预约周期分页数据
    private IPage<Date> getBookingDateList(Integer page, Integer limit, BookingRule bookingRule) {
        //预约周期 int 10
        Integer cycle = bookingRule.getCycle();
        //放号时间
        String releaseTime = bookingRule.getReleaseTime();
        //获得当前放号时间的datetime对象
        DateTime releaseDateTime = getDateTime(new Date(), releaseTime);
        //如果当前时间在放号时间之后,周期加一
        if (releaseDateTime.isBeforeNow()) cycle += 1;
        //可预约的所有日期("yyyy-MM-dd")格式
        List<Date> dateArrayList = new ArrayList<>();
        for (int i = 0; i < cycle; i++) {
            DateTime currentDateTime = new DateTime().plusDays(i);
            String currentDateTimeString = currentDateTime.toString("yyyy-MM-dd");
            dateArrayList.add(new DateTime(currentDateTimeString).toDate());
        }
        //预约周期大于7天,要分页
        int beginIndex = (page - 1) * limit;
        int endIndex = page * limit;
        if (endIndex > dateArrayList.size()) {
            endIndex = dateArrayList.size();
        }
        //当前页日期列表
        ArrayList<Date> dates = new ArrayList<>();
        for (int i = beginIndex; i < endIndex; i++) {
            dates.add(dateArrayList.get(i));
        }
        IPage<Date> datePage = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, limit, dateArrayList.size());//当前页,每页条数,总条数
        datePage.setRecords(dates); //设置当前页数据
        return datePage;
    }

    //将Date日期（yyyy-MM-dd HH:mm）转换为DateTime
    private DateTime getDateTime(Date date, String releaseTime) {
        String dateTimeStr = new DateTime(date).toString("yyyy-MM-dd") + " " + releaseTime;
        return DateTimeFormat.forPattern("yyyy-MM-dd HH:mm").parseDateTime(dateTimeStr);
    }

    private void packageSchedule(Schedule item) {
        //获取医院名称
        String hospitalName = hospitalService.getHospitalName(item.getHoscode());
        //获取科室名称
        String departmentName = departmentService.getDepartmentName(item.getHoscode(), item.getDepcode());
        //日期对应星期
        String dayOfWeek = getDayOfWeek(new DateTime(item.getWorkDate()));
        //封装到param属性中
        item.getParam().put("hosname", hospitalName);
        item.getParam().put("depname", departmentName);
        item.getParam().put("dayOfWeek", dayOfWeek);
    }

    private String getDayOfWeek(DateTime dateTime) {

        String dayOfWeek = "";

        switch (dateTime.getDayOfWeek()) {

            case DateTimeConstants.SUNDAY:

                dayOfWeek = "周日";

                break;

            case DateTimeConstants.MONDAY:

                dayOfWeek = "周一";

                break;

            case DateTimeConstants.TUESDAY:

                dayOfWeek = "周二";

                break;

            case DateTimeConstants.WEDNESDAY:

                dayOfWeek = "周三";

                break;

            case DateTimeConstants.THURSDAY:
                dayOfWeek = "周四";

                break;

            case DateTimeConstants.FRIDAY:

                dayOfWeek = "周五";

                break;

            case DateTimeConstants.SATURDAY:

                dayOfWeek = "周六";

            default:

                break;

        }

        return dayOfWeek;

    }
}
